<!-- codecs.html !-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>VDO.Ninja Codec Support Detector</title>
  <meta id="metaTitle" name="title" content="VDO.Ninja Codec Support Detector">
  <meta name="description" content="Check browser codec support for VDO.Ninja. Detect hardware acceleration for video encoding and decoding.">
  <meta name="author" content="Steve Seguin">
  <meta name="copyright" content="&copy; 2024 Steve Seguin">
  <meta name="license" content="https://github.com/steveseguin/vdo.ninja/LICENSE.md">
  <meta name="sourcecode" content="https://github.com/steveseguin/vdo.ninja">
  <meta name="robots" content="index, follow">
  <link rel="canonical" href="https://vdo.ninja/codec">
  <link rel="author" href="/about">
  
  <meta name="msapplication-TileColor" content="#da532c">
  <meta name="theme-color" content="#0f131d">
  
  <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
  <link id="favicon1" rel="icon" type="image/png" sizes="32x32" href="./media/favicon-32x32.png">
  <link id="favicon2" rel="icon" type="image/png" sizes="16x16" href="./media/favicon-16x16.png">
  <link id="favicon3" rel="icon" href="./media/favicon.ico">
  
  <!-- X (Twitter) Card Tags -->
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:creator" content="@SteveSeguin">
  <meta name="twitter:title" content="VDO.Ninja Codec Support Detector">
  <meta name="twitter:description" content="Check browser codec support for VDO.Ninja. Detect hardware acceleration for video encoding and decoding.">
  <meta name="twitter:image" content="https://vdo.ninja/media/vdoNinja_logo_full.png">
  
  <!-- Open Graph Tags -->
  <meta property="og:type" content="website">
  <meta property="og:url" content="https://vdo.ninja/codec">
  <meta property="og:title" content="VDO.Ninja Codec Support Detector">
  <meta property="og:description" content="Check browser codec support for VDO.Ninja. Detect hardware acceleration for video encoding and decoding.">
  <meta property="og:image" content="https://vdo.ninja/media/vdoNinja_logo_full.png">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta property="og:site_name" content="VDO.Ninja">
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
      line-height: 1.6;
      color: #e0e0e0;
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
      background-color: #121212;
    }
    
    h1, h2, h3 {
      color: #4296f5;
    }
	
	a { 
		color: #4296f5;
	}
    .container {
      background-color: #1e1e1e;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.2);
      padding: 20px;
      margin-bottom: 20px;
    }
    
    .codec-list {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 15px;
      margin-top: 20px;
    }
    
    .codec-item {
      background-color: #2a2a2a;
      border-radius: 6px;
      padding: 15px;
      border-left: 4px solid #4296f5;
      position: relative;
    }
    
    .badge {
      display: inline-block;
      padding: 3px 8px;
      border-radius: 4px;
      font-size: 12px;
      font-weight: bold;
      margin-right: 5px;
      margin-bottom: 5px;
    }
    
    .badge-hw {
      background-color: #34a853;
      color: white;
    }
    
    .badge-sw {
      background-color: #fbbc04;
      color: white;
    }
    
    .badge-recorder {
      background-color: #4296f5;
      color: white;
    }
    
    .badge-webcodec {
      background-color: #ea4335;
      color: white;
    }
    
    .badge-webrtc {
      background-color: #8f44eb;
      color: white;
    }
    
    .badge-mediacapabilities {
      background-color: #ff6d01;
      color: white;
    }
    
    .badge-encoder {
      background-color: #01c0ff;
      color: white;
    }
    
    .badge-decoder {
      background-color: #ff01c0;
      color: white;
    }
    
    .codec-name {
      font-weight: bold;
      margin-bottom: 8px;
    }
    
    .details {
      font-size: 14px;
      color: #aaa;
      margin-top: 10px;
    }
    
    .status-bar {
      height: 4px;
      width: 100%;
      background-color: #333;
      position: relative;
      margin-bottom: 20px;
      border-radius: 2px;
      overflow: hidden;
    }
    
    .status-progress {
      position: absolute;
      height: 100%;
      background-color: #4296f5;
      transition: width 0.3s;
    }
    
    .tabs {
      display: flex;
      margin-bottom: 15px;
      border-bottom: 1px solid #444;
    }
    
    .tab {
      padding: 10px 20px;
      cursor: pointer;
      border-bottom: 3px solid transparent;
      color: #aaa;
    }
    
    .tab.active {
      border-bottom: 3px solid #4296f5;
      font-weight: bold;
      color: #e0e0e0;
    }
    
    .tab-content {
      display: none;
    }
    
    .tab-content.active {
      display: block;
    }
    
    button {
      background-color: #4296f5;
      color: white;
      border: none;
      padding: 10px 20px;
      border-radius: 4px;
      cursor: pointer;
      font-weight: bold;
    }
    
    button:hover {
      background-color: #2979e2;
    }
    
    button:disabled {
      background-color: #555;
      cursor: not-allowed;
    }
    
    #error-message {
      color: #ff5252;
      font-weight: bold;
      margin-top: 10px;
    }
    
    .toggle-container {
      display: flex;
      margin: 15px 0;
      align-items: center;
    }
    
    .toggle-label {
      margin-right: 10px;
    }
    
    .toggle-switch {
      position: relative;
      display: inline-block;
      width: 60px;
      height: 28px;
    }
    
    .toggle-switch input {
      opacity: 0;
      width: 0;
      height: 0;
    }
    
    .toggle-slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #333;
      transition: .4s;
      border-radius: 34px;
    }
    
    .toggle-slider:before {
      position: absolute;
      content: "";
      height: 20px;
      width: 20px;
      left: 4px;
      bottom: 4px;
      background-color: #fff;
      transition: .4s;
      border-radius: 50%;
    }
    
    input:checked + .toggle-slider {
      background-color: #4296f5;
    }
    
    input:checked + .toggle-slider:before {
      transform: translateX(32px);
    }
    
    .toggle-text {
      margin-left: 10px;
    }
    
    pre {
      background: #2a2a2a;
      padding: 15px;
      overflow: auto;
      max-height: 400px;
      color: #e0e0e0;
      border-radius: 4px;
    }
  .codec-guide-container {
    display: flex;
    flex-direction: column;
    gap: 30px;
  }
  
  .codec-section {
    background-color: #2a2a2a;
    border-radius: 8px;
    padding: 20px;
    margin-bottom: 15px;
  }
  
  .codec-section h4 {
    color: #4296f5;
    margin-top: 0;
    margin-bottom: 10px;
    border-bottom: 1px solid #444;
    padding-bottom: 8px;
  }
  
  .usage-block, .tips-block, .note-block, .feature-block {
    background-color: #333;
    border-radius: 6px;
    padding: 15px;
    margin-bottom: 15px;
  }
  
  .usage-header, .tip-header, .note-header, .feature-header {
    font-weight: bold;
    color: #4296f5;
    margin-bottom: 10px;
  }
  
  .usage-example {
    font-family: monospace;
    background-color: #222;
    padding: 8px 12px;
    border-radius: 4px;
    margin-bottom: 8px;
    white-space: nowrap;
    overflow-x: auto;
  }
  
  .options-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    margin-bottom: 15px;
  }
  
  .option-row {
    display: flex;
    border-bottom: 1px solid #444;
  }
  
  .option-row:last-child {
    border-bottom: none;
  }
  
  .header-row {
    background-color: #333;
    font-weight: bold;
  }
  
  .option-cell {
    padding: 10px;
    flex: 1;
  }
  
  .option-cell:first-child {
    flex: 0 0 120px;
  }
  
  .option-cell code {
    background-color: #222;
    padding: 2px 5px;
    border-radius: 3px;
    font-family: monospace;
  }
  
  .tips-list, .note-list, .feature-list {
    margin: 0;
    padding-left: 20px;
  }
  
  .tips-list li, .note-list li, .feature-list li {
    margin-bottom: 8px;
  }
  
  @media (max-width: 768px) {
    .option-row {
      flex-direction: column;
    }
    
    .option-cell {
      padding: 8px;
    }
    
    .option-cell:first-child {
      flex: 1;
      background-color: #333;
      font-weight: bold;
    }
    
    .header-row {
      display: none;
    }
  }
  </style>
</head>
<body>
  <h1>Video Codec Support Detector</h1>
  
  <div class="container">
    <h2>Codec Detection</h2>
    <p>This tool detects video and audio codecs supported by your browser and identifies hardware acceleration capabilities for both encoding and decoding.</p>
    
    <div class="toggle-container">
      <span class="toggle-label">Detection Mode:</span>
      <label class="toggle-switch">
        <input type="checkbox" id="toggle-mode" checked>
        <span class="toggle-slider"></span>
      </label>
      <span class="toggle-text" id="mode-text">Testing Both Encode/Decode</span>
    </div>
    
    <div class="status-bar">
      <div id="status-progress" class="status-progress" style="width: 0%"></div>
    </div>
    
    <button id="start-detection">Start Comprehensive Detection</button>
    <div id="error-message"></div>
  </div>
  
  <div class="container">
    <div class="tabs">
      <div class="tab active" data-tab="video-codecs">Video Codecs</div>
      <div class="tab" data-tab="audio-codecs">Audio Codecs</div>
      <div class="tab" data-tab="raw-data">Raw Data</div>
	  <div class="tab" data-tab="codec-usage">Codec Usage Guide</div>
    </div>
    
    <div id="video-codecs" class="tab-content active">
      <h3>Video Codec Support</h3>
      <div id="video-codec-list" class="codec-list">
        <p>Click "Start Comprehensive Detection" to begin...</p>
      </div>
    </div>
    
    <div id="audio-codecs" class="tab-content">
      <h3>Audio Codec Support</h3>
      <div id="audio-codec-list" class="codec-list">
        <p>Click "Start Comprehensive Detection" to begin...</p>
      </div>
    </div>
    
    <div id="raw-data" class="tab-content">
      <h3>Raw Detection Data</h3>
      <pre id="output" style="background: #2a2a2a; padding: 15px; overflow: auto; max-height: 400px;"></pre>
    </div>
  </div>
  
<div id="codec-usage" class="tab-content">
  <h3>How to Use Codecs in VDO.Ninja</h3>
  
  <div class="codec-guide-container">
    <div class="codec-section">
      <h4>Video Codec Selection (&codec)</h4>
      <p>Control which codec is used to encode and transmit video.</p>
      
      <div class="usage-block">
        <div class="usage-header">Basic Usage</div>
        <div class="usage-example">https://vdo.ninja/?view=abc123&codec=h264</div>
        <div class="usage-example">https://vdo.ninja/?room=xxx7654&scene&bitrate=2000&codec=vp9</div>
      </div>
      
      <div class="options-table">
        <div class="option-row header-row">
          <div class="option-cell">Option</div>
          <div class="option-cell">Description</div>
          <div class="option-cell">Use Case</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>h264</code></div>
          <div class="option-cell">Request H.264 codec</div>
          <div class="option-cell">Better battery life on mobile devices, hardware acceleration on many devices</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>vp8</code></div>
          <div class="option-cell">Request VP8 codec</div>
          <div class="option-cell">Default codec, works on most devices</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>vp9</code></div>
          <div class="option-cell">Request VP9 codec</div>
          <div class="option-cell">Better compression, cleaner image for screen sharing</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>av1</code></div>
          <div class="option-cell">Request AV1 codec</div>
          <div class="option-cell">Most advanced compression, requires Chrome v90+</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>h265</code></div>
          <div class="option-cell">Request H.265/HEVC codec</div>
          <div class="option-cell">High efficiency codec, <a href="https://vdo.ninja/h265" target="_blank">limited browser support</a></div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>webp</code></div>
          <div class="option-cell">Request WebP codec</div>
          <div class="option-cell">Alternative image-based codec</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>hardware</code></div>
          <div class="option-cell">Android-specific option for hardware encoding</div>
          <div class="option-cell">Android devices struggling with video quality</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>av1,h264</code></div>
          <div class="option-cell">Comma-separated fallback options</div>
          <div class="option-cell">Try AV1 first, fall back to H264 if not supported</div>
        </div>
      </div>
      
      <div class="tips-block">
        <div class="tip-header">Selection Tips</div>
        <ul class="tips-list">
          <li><strong>H.264:</strong> Hardware accelerated on many devices, good for mobile battery life</li>
          <li><strong>VP8:</strong> Default choice, software-encoded but compatible with most devices</li>
          <li><strong>VP9:</strong> Better quality at same bitrate vs VP8, but more CPU intensive</li>
          <li><strong>AV1:</strong> Best compression ratio but highest CPU usage, limited device support</li>
          <li><strong>H.265:</strong> Limited browser support, needs <a href="https://vdo.ninja/h265" target="_blank">command-line flags in Chrome</a></li>
        </ul>
      </div>
      
      <div class="note-block">
        <div class="note-header">Important Notes</div>
        <ul class="note-list">
          <li>The <code>&codec</code> parameter is added to the viewer-side (use with <code>&view</code> or <code>&scene</code>)</li>
          <li>Hardware encoding capabilities vary by device, OS, and browser.</li>
          <li>H.264 hardware encoding might use less battery but sometimes has compatibility issues</li>
          <li>AV1 and VP9 tends to look better for screen sharing but uses more CPU</li>
		  <li>AV1 tends to offer more accurate colours; useful for chroma green screening</li>
          <li>Use the detection tool above to see which codecs have hardware acceleration on your device</li>
        </ul>
      </div>
    </div>
    
    <div class="codec-section">
      <h4>Recording Options (&record)</h4>
      <p>Configure how video and audio are recorded to disk.</p>
      
      <div class="usage-block">
        <div class="usage-header">Basic Usage</div>
        <div class="usage-example">https://vdo.ninja/?push=abc123&record=2000</div>
        <div class="usage-example">https://vdo.ninja/?push=xxx7654&record=0</div>
      </div>
      
      <div class="options-table">
        <div class="option-row header-row">
          <div class="option-cell">Option</div>
          <div class="option-cell">Description</div>
          <div class="option-cell">Use Case</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>0</code></div>
          <div class="option-cell">No video, audio recorded as 32bit PCM lossless</div>
          <div class="option-cell">High-quality audio only recording</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>-120</code> (negative)</div>
          <div class="option-cell">No video, audio at specified kbps (OPUS)</div>
          <div class="option-cell">Audio-only recording with specific bitrate</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>2000</code> (positive)</div>
          <div class="option-cell">Video bitrate in kbps</div>
          <div class="option-cell">Video recording with specific quality setting</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>false</code> or <code>off</code></div>
          <div class="option-cell">Disable recording feature</div>
          <div class="option-cell">Prevents user from recording</div>
        </div>
      </div>
      
      <div class="note-block">
        <div class="note-header">Recording Notes</div>
        <ul class="note-list">
          <li>Recorded file format is WebM with VP8/H264 video and OPUS/PCM audio</li>
          <li>Default bitrate is approximately 4000 kbps if not specified</li>
          <li>The director of a room will be notified when a user is recording</li>
          <li>The director can trigger recording remotely</li>
          <li>Video/audio is saved in real-time to the local download folder</li>
          <li>Recording should be stopped manually before closing the browser</li>
        </ul>
      </div>
    </div>
    
    <div class="codec-section">
      <h4>Recording Codec Selection (&recordcodec)</h4>
      <p>Set the specific codec used when recording to disk.</p>
      
      <div class="usage-block">
        <div class="usage-header">Basic Usage</div>
        <div class="usage-example">https://vdo.ninja/?push=abc123&record=2000&recordcodec=h264</div>
      </div>
      
      <div class="options-table">
        <div class="option-row header-row">
          <div class="option-cell">Option</div>
          <div class="option-cell">Description</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>h264</code></div>
          <div class="option-cell">Record using H.264 codec</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>vp8</code></div>
          <div class="option-cell">Record using VP8 codec (default fallback)</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>vp9</code></div>
          <div class="option-cell">Record using VP9 codec</div>
        </div>
        <div class="option-row">
          <div class="option-cell"><code>av1</code></div>
          <div class="option-cell">Record using AV1 codec</div>
        </div>
      </div>
      
      <div class="note-block">
        <div class="note-header">Important Notes</div>
        <ul class="note-list">
          <li>The container format is always WebM regardless of codec, unless using Safari, and maybe then its MP4</li>
          <li>If a codec is not supported, it will fall back to VP8</li>
          <li>Especially useful for Chrome on Android where VP8 performance can be poor</li>
          <li>Remember to add <code>&record</code> to enable the recording function</li>
          <li>Use <code>&rc</code> as a shorter alias for <code>&recordcodec</code></li>
        </ul>
      </div>
    </div>
    
    <div class="codec-section">
      <h4>Advanced WebRTC Features</h4>
      <p>WebRTC supports additional features like SVC (Scalable Video Coding) and WHiP.</p>
      
      <div class="feature-block">
        <div class="feature-header">SVC (Scalable Video Coding)</div>
        <p>SVC allows a single bitstream to have multiple resolutions, frame rates, or quality layers.</p>
        <ul class="feature-list">
          <li>Improves streaming adaptability for viewers with different connection qualities</li>
          <li>Support varies by browser and codec</li>
          <li>Common modes include L1T1 (single layer), L2T1 (2 spatial layers), S2T1 (2 temporal layers)</li>
          <li>The detection tool above shows supported SVC modes for your browser</li>
        </ul>
      </div>
      
      <div class="feature-block">
        <div class="feature-header">WHiP (WebRTC HTTP ingest Protocol)</div>
        <p>WHiP is a standardized protocol for sending WebRTC streams to a server.</p>
        <ul class="feature-list">
          <li>Enables WebRTC streaming to CDNs and streaming platforms</li>
          <li>Simpler to implement than custom signaling servers</li>
          <li>Useful for broadcast scenarios with many viewers</li>
          <li>Check your streaming platform documentation for WHiP support details</li>
        </ul>
      </div>
      <div class="note-block">
        <div class="note-header">H.265/HEVC Support</div>
        <p>H.265 (HEVC) offers excellent compression efficiency but has limited browser support. <a href="https://vdo.ninja/h265" target="_blank">Visit our H.265 guide</a> for detailed instructions on:</p>
        <ul class="note-list">
          <li>Enabling H.265 in Chrome using command-line flags</li>
          <li>Browser compatibility information</li>
          <li>Fallback strategies using comma-separated codec preferences</li>
          <li>Testing your browser's H.265 support</li>
        </ul>
      </div>
    </div>
  </div>
</div>
 
  <script>
    // Main data object to store all detection results
    const codecData = {
      video: {}, // Will store video codec info
      audio: {}, // Will store audio codec info
      webrtc: {}, // Will store WebRTC related info
      webcodec: {}, // Will store WebCodec related info
      mediaCapabilities: {} // Will store MediaCapabilities info
    };
    
    // Basic DOM manipulation helpers
    const dom = {
      get: id => document.getElementById(id),
      create: tag => document.createElement(tag),
      append: (parent, child) => parent.appendChild(child)
    };
    
    // Detection mode
    let detectionMode = {
      encode: true,
      decode: true
    };
    
    // Toggle switch for detection mode
    dom.get('toggle-mode').addEventListener('change', function() {
      if (this.checked) {
        detectionMode = { encode: true, decode: true };
        dom.get('mode-text').textContent = 'Testing Both Encode/Decode';
      } else {
        detectionMode = { encode: false, decode: true };
        dom.get('mode-text').textContent = 'Testing Decode Only';
      }
    });
    
    // Tab functionality
    document.querySelectorAll('.tab').forEach(tab => {
	  tab.addEventListener('click', () => {
		// Remove active class from all tabs and contents
		document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
		document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
		
		// Add active class to clicked tab and corresponding content
		tab.classList.add('active');
		dom.get(tab.dataset.tab).classList.add('active');
	  });
	});
	
	// Add this to the codecs.html file, inside the script tag, near the top

	// Check for results parameter
	function checkForResultsParam() {
	  const urlParams = new URLSearchParams(window.location.search);
	  const resultsId = urlParams.get('results');
	  
	  if (resultsId) {
		loadResultsData(resultsId);
		
		// Update the page header
		const pageHeader = document.querySelector('h1');
		if (pageHeader) {
		  pageHeader.textContent = 'Loading Remote User Codec Support...';
		}
		
		// Disable the start button
		const startButton = dom.get('start-detection');
		if (startButton) {
		  startButton.disabled = true;
		  startButton.textContent = 'Loading Remote Results...';
		}
	  }
	}

	// Load results data from the server
	async function loadResultsData(resultsId) {
	  try {
		// Show progress indicator
		setProgress(30);
		
		// Fetch the results data
		const response = await fetch(`https://record.vdo.workers.dev/?name=${resultsId}`);
		
		if (!response.ok) {
		  throw new Error(`Failed to load results (${response.status})`);
		}
		
		const results = await response.json();
		setProgress(60);
		
		// Process the results
		processResultsData(results);
		
		// Update the UI with the loaded data
		updateUI();
		setProgress(100);
		
		// Update the page header
		const pageHeader = document.querySelector('h1');
		if (pageHeader) {
		  pageHeader.textContent = 'Remote User Codec Support';
		}
		
		// Update the button text
		const startButton = dom.get('start-detection');
		if (startButton) {
		  startButton.textContent = 'Run Local Detection Instead';
		  startButton.disabled = false;
		}
		
		// Add a "back to results" link
		const container = document.querySelector('.container');
		if (container) {
		  const backLink = dom.create('div');
		  backLink.className = 'back-link';
		  backLink.innerHTML = `<a href="./results?id=${resultsId}">← Back to Test Results</a>`;
		  container.insertBefore(backLink, container.firstChild);
		  
		  // Add notification that we're viewing remote data
		  const remoteNotice = dom.create('div');
		  remoteNotice.className = 'remote-notice';
		  remoteNotice.textContent = 'You are viewing codec information from a remote user\'s test results';
		  container.insertBefore(remoteNotice, container.firstChild);
		  
		  // Add styles
		  const style = dom.create('style');
		  style.textContent = `
			.back-link {
			  margin-bottom: 15px;
			}
			.back-link a {
			  color: #4296f5;
			  text-decoration: none;
			  display: inline-flex;
			  align-items: center;
			}
			.back-link a:hover {
			  text-decoration: underline;
			}
			.remote-notice {
			  background-color: #2a2a2a;
			  border-left: 4px solid #4296f5;
			  padding: 10px 15px;
			  margin-bottom: 15px;
			  font-weight: bold;
			}
		  `;
		  document.head.appendChild(style);
		}
		
	  } catch (error) {
		console.error('Error loading results:', error);
		showError(`Failed to load results: ${error.message}`);
		
		// Re-enable the start button on error
		const startButton = dom.get('start-detection');
		if (startButton) {
		  startButton.disabled = false;
		  startButton.textContent = 'Start Comprehensive Detection';
		}
	  }
	}

	// Process the loaded results data
	function processResultsData(results) {
	  // Clear existing codec data
	  codecData.video = {};
	  codecData.audio = {};
	  
	  // Look for codec information in the results
	  for (const item of results) {
		// Look for detectedCodecs data (if it exists in the logged data)
		if (item.detectedCodecs) {
		  // Copy the codec information
		  Object.assign(codecData.video, item.detectedCodecs.video || {});
		  Object.assign(codecData.audio, item.detectedCodecs.audio || {});
		  
		  // Log info about the remote browser
		  if (item.detectedCodecs.browserInfo) {
			addLog(`Remote Browser: ${item.detectedCodecs.browserInfo.userAgent || "Unknown"}`);
		  }
		}
		
		// Look for codecs data - the format used in the test data
		if (item.codecs) {
		  // Process video codecs
		  if (item.codecs.video) {
			Object.keys(item.codecs.video).forEach(codec => {
			  codecData.video[codec] = codecData.video[codec] || {};
			  codecData.video[codec].webrtc = true;
			  codecData.video[codec].canDecode = true;
			  
			  // Add a badge or icon for these values in the UI
			  if (codec === 'h264' || codec === 'vp9') {
				codecData.video[codec].details = 'Common codec for streaming';
			  }
			});
			
			addLog(`Remote Video Codecs: ${Object.keys(item.codecs.video).join(', ')}`);
		  }
		  
		  // Process audio codecs
		  if (item.codecs.audio) {
			Object.keys(item.codecs.audio).forEach(codec => {
			  codecData.audio[codec] = codecData.audio[codec] || {};
			  codecData.audio[codec].webrtc = true;
			  codecData.audio[codec].canDecode = true;
			});
			
			addLog(`Remote Audio Codecs: ${Object.keys(item.codecs.audio).join(', ')}`);
		  }
		}
		
		// Look for browser/platform info
		if (item.info) {
		  if (item.info.Browser) {
			addLog(`Browser: ${item.info.Browser}`);
		  }
		  if (item.info.platform) {
			addLog(`Platform: ${item.info.platform}`);
		  }
		  if (item.info.gpGPU) {
			addLog(`GPU: ${item.info.gpGPU}`);
			
			// Set additional details for hardware acceleration based on GPU
			if (codecData.video.h264) {
			  codecData.video.h264.details = `GPU: ${item.info.gpGPU}`;
			}
		  }
		  if (item.info.CPU) {
			addLog(`CPU: ${item.info.CPU}`);
		  }
		}
		
		// Look for encoder used
		if (item.encoder) {
		  const encoderName = item.encoder.toLowerCase().replace('libvpx', 'vp8');
		  addLog(`Encoder used in test: ${encoderName}`);
		  
		  // Mark this codec as used in the test
		  if (codecData.video[encoderName]) {
			codecData.video[encoderName].usedInTest = true;
			codecData.video[encoderName].details = `Used during the test`;
		  }
		}
	  }
	}

	// Call this function when the page loads
	document.addEventListener('DOMContentLoaded', checkForResultsParam);
    
    // Set progress bar
    function setProgress(percent) {
      dom.get('status-progress').style.width = `${percent}%`;
    }
    
    // Show error message
    function showError(message) {
      dom.get('error-message').textContent = message;
    }
    
    // Add log to raw data tab
    function addLog(message, isError = false) {
      const output = dom.get('output');
      const logEntry = document.createElement('div');
      logEntry.textContent = message;
      if (isError) logEntry.style.color = '#ff5252';
      output.appendChild(logEntry);
      output.scrollTop = output.scrollHeight;
    }
    
// Create a codec item element with more detailed information
function createCodecItem(codec, info) {
  const item = dom.create('div');
  item.className = 'codec-item';
  
  const codecName = dom.create('div');
  codecName.className = 'codec-name';
  codecName.textContent = codec;
  
  const badgesContainer = dom.create('div');
  
  // Add API badges
  if (info.mediaRecorder) {
    const badge = dom.create('span');
    badge.className = 'badge badge-recorder';
    badge.textContent = 'MediaRecorder';
    badgesContainer.appendChild(badge);
  }
  
  if (info.webcodec) {
    const badge = dom.create('span');
    badge.className = 'badge badge-webcodec';
    badge.textContent = 'WebCodec';
    badgesContainer.appendChild(badge);
  }
  
  if (info.webrtc) {
    const badge = dom.create('span');
    badge.className = 'badge badge-webrtc';
    badge.textContent = 'WebRTC';
    badgesContainer.appendChild(badge);
  }
  
  if (info.mediaCapabilities) {
    const badge = dom.create('span');
    badge.className = 'badge badge-mediacapabilities';
    badge.textContent = 'MediaCapabilities';
    badgesContainer.appendChild(badge);
  }
  
  // Add encoder/decoder badges with specific API info
  if (info.canEncode) {
    const badge = dom.create('span');
    badge.className = 'badge badge-encoder';
    badge.textContent = 'Encoder';
    badgesContainer.appendChild(badge);
  }
  
  if (info.canDecode) {
    const badge = dom.create('span');
    badge.className = 'badge badge-decoder';
    badge.textContent = 'Decoder';
    badgesContainer.appendChild(badge);
  }
  
  // Create the details container for more specific information
  const details = dom.create('div');
  details.className = 'details';
  
  // Add more specific hardware acceleration info
  if (info.webcodecConfig && info.webcodecConfig.encoder && info.webcodecConfig.encoder.hardwareAcceleration === 'prefer-hardware') {
    const hwEncBadge = dom.create('span');
    hwEncBadge.className = 'badge badge-hw';
    hwEncBadge.textContent = 'HW Encoder';
    details.appendChild(hwEncBadge);
  }
  
  if (info.webcodecConfig && info.webcodecConfig.decoder && info.webcodecConfig.decoder.hardwareAcceleration === 'prefer-hardware') {
    const hwDecBadge = dom.create('span');
    hwDecBadge.className = 'badge badge-hw';
    hwDecBadge.textContent = 'HW Decoder';
    details.appendChild(hwDecBadge);
  }
  
  if (info.webrtcHwEncoding) {
    const hwWrtcEncBadge = dom.create('span');
    hwWrtcEncBadge.className = 'badge badge-hw';
    hwWrtcEncBadge.textContent = 'WebRTC HW Encoder';
    details.appendChild(hwWrtcEncBadge);
  }
  
  // Add special details if available
  if (info.profile) {
    details.innerHTML += `<div>Profile: ${info.profile}</div>`;
  }
  
  if (info.resolutionLimit) {
    details.innerHTML += `<div>Resolution Limit: ${info.resolutionLimit}</div>`;
  }
  
  if (info.details) {
    details.innerHTML += `<div>${info.details}</div>`;
  }
  
  if (info.scalabilityModes && info.scalabilityModes.length) {
    details.innerHTML += `<div>SVC Modes: ${info.scalabilityModes.join(', ')}</div>`;
  }
  
  item.appendChild(codecName);
  item.appendChild(badgesContainer);
  item.appendChild(details);
  
  return item;
}
    
    // Get all supported MIME types via MediaRecorder
    function getSupportedMimeTypes(media, types, codecs) {
      const supported = [];
      
      // First check simple types
      types.forEach(type => {
        const mimeType = `${media}/${type}`;
        if (MediaRecorder.isTypeSupported(mimeType)) {
          supported.push(mimeType);
        }
      });
      
      // Then check with codecs
      types.forEach(type => {
        const mimeType = `${media}/${type}`;
        
        codecs.forEach(codec => {
          if (!codec) return;
          
          const variation = `${mimeType};codecs=${codec.toLowerCase()}`;
          if (MediaRecorder.isTypeSupported(variation)) {
            supported.push(variation);
          }
        });
        
        // Check codec combinations for video/webm
        if (media === 'video' && type === 'webm') {
          codecs.forEach(videoCodec => {
            if (!videoCodec) return;
            
            const audioCodecs = ['opus', 'vorbis'];
            audioCodecs.forEach(audioCodec => {
              const variation = `${mimeType};codecs=${videoCodec.toLowerCase()},${audioCodec}`;
              if (MediaRecorder.isTypeSupported(variation)) {
                supported.push(variation);
              }
            });
          });
        }
      });
      
      return supported;
    }
    
    // Check hardware acceleration via WebRTC
    async function checkWebRTCHardwareAcceleration(codec) {
      return new Promise(async (resolve) => {
        try {
          // Create peer connection with reasonable config (no TURN servers needed)
          const config = {
            iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
            iceCandidatePoolSize: 0
          };
          
          const pc1 = new RTCPeerConnection(config);
          const pc2 = new RTCPeerConnection(config);
          
          // Create data channel to keep connection alive
          pc1.createDataChannel('test', {ordered: true});
          
          // Connect the peers
          pc1.onicecandidate = e => e.candidate && pc2.addIceCandidate(e.candidate);
          pc2.onicecandidate = e => e.candidate && pc1.addIceCandidate(e.candidate);
          
          // Create a video stream from canvas to avoid permission prompts
          const canvas = document.createElement('canvas');
          canvas.width = 1280;
          canvas.height = 720;
          
          // Draw something on the canvas to ensure there's content
          const ctx = canvas.getContext('2d');
          ctx.fillStyle = '#3498db';
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          
          // Add some animation to ensure proper encoding
          const startTime = Date.now();
          function animateCanvas() {
            const elapsed = Date.now() - startTime;
            ctx.fillStyle = '#3498db';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // Draw a moving circle
            ctx.fillStyle = '#e74c3c';
            const x = 100 + Math.sin(elapsed / 500) * 100;
            const y = canvas.height / 2 + Math.cos(elapsed / 500) * 100;
            ctx.beginPath();
            ctx.arc(x, y, 50, 0, Math.PI * 2);
            ctx.fill();
            
            if (Date.now() - startTime < 3000) {
              requestAnimationFrame(animateCanvas);
            }
          }
          
          animateCanvas();
          const stream = canvas.captureStream(30);
          
          // Add tracks to the connection
          stream.getVideoTracks().forEach(track => {
            pc1.addTrack(track, stream);
          });
          
          // Create and set the offer with specific codec
          const transceiver = pc1.getTransceivers()[0];
          const codecs = RTCRtpSender.getCapabilities('video').codecs;
          
          // Find matching codec
          const targetCodec = codecs.find(c => {
            return c.mimeType.toLowerCase().includes(codec.toLowerCase());
          });
          
          if (targetCodec) {
            transceiver.setCodecPreferences([targetCodec]);
          }
          
          const offer = await pc1.createOffer();
          await pc1.setLocalDescription(offer);
          await pc2.setRemoteDescription(offer);
          const answer = await pc2.createAnswer();
          await pc2.setLocalDescription(answer);
          await pc1.setRemoteDescription(answer);
          
          // Wait a bit for stats to be available
          setTimeout(async () => {
            let hardwareAccelerated = false;
            let profile = null;
            
            try {
              const stats = await pc1.getStats();
              stats.forEach(stat => {
                // Check for hardware encoding
                if (stat.type === 'outbound-rtp' && stat.kind === 'video') {
                  if (stat.encoderImplementation) {
                    const implementation = stat.encoderImplementation.toLowerCase();
                    hardwareAccelerated = implementation.includes('hardware') || 
                                          implementation === 'externalencoder' || 
                                          implementation === 'mediafoundationvideoacceleration' ||
                                          implementation.includes('accelerator');
                  }
                  
                  // Try to get the profile ID
                  if (stat.codecId) {
                    stats.forEach(s => {
                      if (s.id === stat.codecId && s.sdpFmtpLine) {
                        const match = s.sdpFmtpLine.match(/profile-level-id=([0-9a-f]+)/i);
                        if (match) {
                          profile = match[1];
                        }
                      }
                    });
                  }
                }
              });
            } catch (e) {
              addLog(`Error getting WebRTC stats: ${e.message}`, true);
            }
            
            // Clean up
            pc1.close();
            pc2.close();
            stream.getTracks().forEach(track => track.stop());
            
            resolve({ 
				hardwareAccelerated, 
				profile,
				webrtc: true,
				canEncode: true,
				canDecode: true,
				webrtcHwEncoding: hardwareAccelerated // specific flag for WebRTC encoding
			  });
          }, 2000);
        } catch (error) {
          addLog(`WebRTC check failed for ${codec}: ${error.message}`, true);
          resolve({ webrtc: false });
        }
      });
    }
    
    // Check hardware acceleration via WebCodec API for encoding
    async function checkWebCodecEncoder(codec, width = 1280, height = 720, framerate = 30) {
      if (!('VideoEncoder' in window) || !detectionMode.encode) {
        return { webcodec: false };
      }
      
      let result = { webcodec: false };
      
      try {
        // Map common codec names to WebCodec format
        const codecMapping = {
          'vp8': 'vp8',
          'vp9': 'vp09.00.10.08',
          'av1': 'av01.0.04M.08',
          'h264': 'avc1.42001E',
          'h265': 'hev1.1.6.L93.B0'
        };
        
        const webcodecFormat = codecMapping[codec.toLowerCase()] || codec;
        
        // Check hardware acceleration with prefer-hardware
        const hwConfig = {
          codec: webcodecFormat,
          hardwareAcceleration: 'prefer-hardware',
          width,
          height,
          bitrate: 2000000,
          framerate
        };
        
        const hwSupport = await VideoEncoder.isConfigSupported(hwConfig);
        
        if (hwSupport && hwSupport.supported) {
          result = { 
            webcodec: true,
            hardwareAccelerated: true,
            config: hwSupport.config,
            canEncode: true
          };
        } else {
          // Check with prefer-software
          const swConfig = {
            codec: webcodecFormat,
            hardwareAcceleration: 'prefer-software',
            width,
            height,
            bitrate: 2000000,
            framerate
          };
          
          const swSupport = await VideoEncoder.isConfigSupported(swConfig);
          
          if (swSupport && swSupport.supported) {
            result = { 
              webcodec: true,
              hardwareAccelerated: false,
              config: swSupport.config,
              canEncode: true
            };
          }
        }
      } catch (error) {
        addLog(`WebCodec encoder check failed for ${codec}: ${error.message}`, true);
      }
      
      return result;
    }
    
    // Check hardware acceleration via WebCodec API for decoding
    async function checkWebCodecDecoder(codec, width = 1280, height = 720, framerate = 30) {
      if (!('VideoDecoder' in window) || !detectionMode.decode) {
        return { webcodec: false };
      }
      
      let result = { webcodec: false };
      
      try {
        // Map common codec names to WebCodec format
        const codecMapping = {
          'vp8': 'vp8',
          'vp9': 'vp09.00.10.08',
          'av1': 'av01.0.04M.08',
          'h264': 'avc1.42001E',
          'h265': 'hev1.1.6.L93.B0'
        };
        
        const webcodecFormat = codecMapping[codec.toLowerCase()] || codec;
        
        // Check hardware acceleration with prefer-hardware
        const hwConfig = {
          codec: webcodecFormat,
          hardwareAcceleration: 'prefer-hardware',
          codedWidth: width,
          codedHeight: height,
          description: new Uint8Array(0) // Dummy data
        };
        
        const hwSupport = await VideoDecoder.isConfigSupported(hwConfig);
        
        if (hwSupport && hwSupport.supported) {
          result = { 
            webcodec: true,
            hardwareAccelerated: true,
            config: hwSupport.config,
            canDecode: true
          };
        } else {
          // Check with prefer-software
          const swConfig = {
            codec: webcodecFormat,
            hardwareAcceleration: 'prefer-software',
            codedWidth: width,
            codedHeight: height,
            description: new Uint8Array(0) // Dummy data
          };
          
          const swSupport = await VideoDecoder.isConfigSupported(swConfig);
          
          if (swSupport && swSupport.supported) {
            result = { 
              webcodec: true,
              hardwareAccelerated: false,
              config: swSupport.config,
              canDecode: true
            };
          }
        }
      } catch (error) {
        addLog(`WebCodec decoder check failed for ${codec}: ${error.message}`, true);
      }
      
      return result;
    }
    
		  // Comprehensive codec detection
		async function detectCodecs() {
		  setProgress(0);
		  showError('');
		  dom.get('output').innerHTML = '';
		  dom.get('video-codec-list').innerHTML = '<p>Detection in progress...</p>';
		  dom.get('audio-codec-list').innerHTML = '<p>Detection in progress...</p>';
		  
		  // Disable the start button during detection
		  const startButton = dom.get('start-detection');
		  startButton.disabled = true;
		  startButton.textContent = 'Detection in progress...';
		  
		  try {
			// 1. Define codec lists
			addLog('Starting codec detection...');
			addLog(`Mode: ${detectionMode.encode ? 'Encoding + ' : ''}${detectionMode.decode ? 'Decoding' : ''}`);
			
			const videoTypes = ["webm", "mp4", "x-matroska", "ogg"];
			const audioTypes = ["webm", "mp4", "ogg", "x-matroska", "wav"];
			
			const videoCodecs = [
			  "vp8", "vp9", "av1", "h264", "h265", 
			  "vp9.0", "vp8.0", "avc1", "av1x", "h.264", "h.265", 
			  "av01.0.04M.08", "vp09.00.10.08", "avc1.42001E"
			];
			
			const audioCodecs = [
			  "opus", "vorbis", "mp3", "aac", "pcm", 
			  "mp4a.40.2", "mp4a", "L16", "wav", "flac"
			];
			
			setProgress(10);
			
			// 2. Detect MediaRecorder support
			addLog('Checking MediaRecorder support...');
			
			const supportedVideoTypes = getSupportedMimeTypes("video", videoTypes, videoCodecs);
			const supportedAudioTypes = getSupportedMimeTypes("audio", audioTypes, audioCodecs);
			
			supportedVideoTypes.forEach(type => {
			  const codecMatch = type.match(/codecs=([^,]+)/);
			  const codec = codecMatch ? codecMatch[1] : type.split('/')[1];
			  
			  codecData.video[codec] = codecData.video[codec] || {};
			  codecData.video[codec].mediaRecorder = true;
			  codecData.video[codec].canEncode = true;
			  codecData.video[codec].mimeTypes = codecData.video[codec].mimeTypes || [];
			  codecData.video[codec].mimeTypes.push(type);
			});
			
			supportedAudioTypes.forEach(type => {
			  const codecMatch = type.match(/codecs=([^,]+)/);
			  const codec = codecMatch ? codecMatch[1] : type.split('/')[1];
			  
			  codecData.audio[codec] = codecData.audio[codec] || {};
			  codecData.audio[codec].mediaRecorder = true;
			  codecData.audio[codec].canEncode = true;
			  codecData.audio[codec].mimeTypes = codecData.audio[codec].mimeTypes || [];
			  codecData.audio[codec].mimeTypes.push(type);
			});
			
			addLog(`Found ${supportedVideoTypes.length} supported video MediaRecorder formats`);
			addLog(`Found ${supportedAudioTypes.length} supported audio MediaRecorder formats`);
			
			setProgress(30);
			
			// 3. Detect WebCodec support
			if ('VideoEncoder' in window || 'VideoDecoder' in window) {
			  addLog('Checking WebCodec support...');
			  
			  // Check at multiple resolutions to detect resolution-dependent hardware acceleration
			  const resolutions = [
				{ width: 640, height: 360 },
				{ width: 1280, height: 720 },
				{ width: 1920, height: 1080 }
			  ];
			  
			  for (const codec of ['vp8', 'vp9', 'h264', 'av1', 'h265']) {
				for (const res of resolutions) {
				  // Check encoder if enabled
				  if (detectionMode.encode) {
					const encoderResult = await checkWebCodecEncoder(codec, res.width, res.height, 30);
					
					if (encoderResult.webcodec) {
					  codecData.video[codec] = codecData.video[codec] || {};
					  codecData.video[codec].webcodec = true;
					  codecData.video[codec].canEncode = true;
					  
					  // Store specific hardware acceleration for WebCodec encoder
					  codecData.video[codec].webcodecHwEncoding = encoderResult.hardwareAccelerated;
					  
					  // If any resolution has hardware acceleration, we'll mark it as supported
					  if (encoderResult.hardwareAccelerated) {
						codecData.video[codec].hardwareAccelerated = true;
						
						// Store the min resolution that allows hardware acceleration
						if (!codecData.video[codec].minHwResolution || 
							res.width * res.height < 
							codecData.video[codec].minHwResolution.width * codecData.video[codec].minHwResolution.height) {
						  codecData.video[codec].minHwResolution = res;
						  codecData.video[codec].resolutionLimit = `${res.width}x${res.height}+`;
						}
					  } 
					  // Only set hardware as false if it hasn't been set to true already
					  else if (codecData.video[codec].hardwareAccelerated !== true) {
						codecData.video[codec].hardwareAccelerated = false;
					  }
					  
					  if (encoderResult.config) {
						codecData.video[codec].webcodecConfig = codecData.video[codec].webcodecConfig || {};
						codecData.video[codec].webcodecConfig.encoder = encoderResult.config;
					  }
					}
				  }
				  
				  // Check decoder if enabled
				  if (detectionMode.decode) {
					const decoderResult = await checkWebCodecDecoder(codec, res.width, res.height, 30);
					
					if (decoderResult.webcodec) {
					  codecData.video[codec] = codecData.video[codec] || {};
					  codecData.video[codec].webcodec = true;
					  codecData.video[codec].canDecode = true;
					  
					  // Store specific hardware acceleration for WebCodec decoder
					  codecData.video[codec].webcodecHwDecoding = decoderResult.hardwareAccelerated;
					  
					  // If any resolution has hardware acceleration, we'll mark it as supported
					  if (decoderResult.hardwareAccelerated) {
						codecData.video[codec].hardwareAccelerated = true;
						
						// Store the min resolution that allows hardware acceleration
						if (!codecData.video[codec].minHwResolution || 
							res.width * res.height < 
							codecData.video[codec].minHwResolution.width * codecData.video[codec].minHwResolution.height) {
						  codecData.video[codec].minHwResolution = res;
						  codecData.video[codec].resolutionLimit = `${res.width}x${res.height}+`;
						}
					  } 
					  // Only set hardware as false if it hasn't been set to true already
					  else if (codecData.video[codec].hardwareAccelerated !== true) {
						codecData.video[codec].hardwareAccelerated = false;
					  }
					  
					  if (decoderResult.config) {
						codecData.video[codec].webcodecConfig = codecData.video[codec].webcodecConfig || {};
						codecData.video[codec].webcodecConfig.decoder = decoderResult.config;
					  }
					}
				  }
				}
			  }
			} else {
			  addLog('WebCodec API not supported by this browser');
			}
			
			setProgress(60);
			
			// 4. Detect WebRTC support and hardware acceleration
			if ('RTCPeerConnection' in window) {
			  addLog('Checking WebRTC support and hardware acceleration...');
			  
			  // Codecs most commonly used in WebRTC
			  const webrtcCodecs = ['VP8', 'VP9', 'AV1', 'H264'];
			  
			  if (detectionMode.encode || detectionMode.decode) {
				for (const codec of webrtcCodecs) {
				  const result = await checkWebRTCHardwareAcceleration(codec);
				  
				  if (result.webrtc) {
					const normalizedCodec = codec.toLowerCase();
					codecData.video[normalizedCodec] = codecData.video[normalizedCodec] || {};
					codecData.video[normalizedCodec].webrtc = true;
					
					if (detectionMode.encode) {
					  codecData.video[normalizedCodec].canEncode = true;
					}
					
					if (detectionMode.decode) {
					  codecData.video[normalizedCodec].canDecode = true;
					}
					
					// Only update hardware acceleration if not already set
					if (result.hardwareAccelerated) {
					  codecData.video[normalizedCodec].hardwareAccelerated = true;
					} else if (codecData.video[normalizedCodec].hardwareAccelerated !== true) {
					  codecData.video[normalizedCodec].hardwareAccelerated = false;
					}
					
					if (result.profile) {
					  codecData.video[normalizedCodec].profile = result.profile;
					}
				  }
				}
			  }
			} else {
			  addLog('WebRTC not supported by this browser');
			}
			
			setProgress(90);
			
			// 5. Update the UI with all collected data
			updateUI();
			
			// 6. Check MediaCapabilities API
			if ('mediaCapabilities' in navigator) {
			  addLog('Checking MediaCapabilities API support...');
			  
			  const videoTypes = ['vp8', 'vp9', 'av1', 'h264', 'h265', 'avc1'];
			  const resolutions = [
				{ width: 640, height: 360 },
				{ width: 1280, height: 720 },
				{ width: 1920, height: 1080 }
			  ];
			  
			  // Define scalability modes to test
			  const scalabilityModes = [
				"L1T1", "L1T2", "L1T3",
				"L2T1", "L2T2", "L2T3",
				"L3T1", "L3T2", "L3T3",
				"S2T1", "S2T2", "S2T3",
				"S3T1", "S3T2", "S3T3",
				"L1T2h", "L1T3h",
				"L2T1h", "L2T2h", "L2T3h",
				"L3T1h", "L3T2h", "L3T3h"
			  ];
			  
			  // Check browser-specific capability type
			  const isFirefox = navigator.userAgent.indexOf("Firefox") >= 0;
			  const capabilityType = isFirefox ? "transmission" : "webrtc";
			  
			  // Check WebRTC codec capabilities
			  const webrtcCodecs = [];
			  if ('RTCRtpSender' in window && RTCRtpSender.getCapabilities) {
				try {
				  const rtcCodecs = RTCRtpSender.getCapabilities('video').codecs;
				  rtcCodecs.forEach(codec => {
					if (!['video/red', 'video/ulpfec', 'video/rtx'].includes(codec.mimeType)) {
					  const codecName = codec.mimeType.replace("video/", "").toLowerCase();
					  webrtcCodecs.push({
						name: codecName,
						mimeType: codec.mimeType,
						sdpFmtpLine: codec.sdpFmtpLine,
						clockRate: codec.clockRate
					  });
					}
				  });
				  
				  addLog(`Found ${webrtcCodecs.length} supported WebRTC codecs`);
				} catch (e) {
				  addLog(`Error getting RTCRtpSender.getCapabilities: ${e.message}`, true);
				}
			  }
			  
			  // Process each codec with MediaCapabilities
			  for (const codec of webrtcCodecs) {
				try {
				  const capabilityPromises = [];
				  const codecScalabilityModes = [];
				  
				  // Test different scalability modes
				  for (const mode of scalabilityModes) {
					for (const res of resolutions) {
					  capabilityPromises.push(
						navigator.mediaCapabilities.encodingInfo({
						  type: capabilityType,
						  video: {
							contentType: codec.mimeType,
							width: res.width,
							height: res.height,
							bitrate: 2000000,
							framerate: 30,
							scalabilityMode: mode
						  }
						}).then(result => {
						  return { mode, res, result };
						}).catch(e => {
						  return { mode, res, error: e };
						})
					  );
					}
				  }
				  
				  const results = await Promise.all(capabilityPromises);
				  
				  // Process results
				  let isSupported = false;
				  let isSmooth = false;
				  let isPowerEfficient = false;
				  let supportedModes = [];
				  
				  results.forEach(({ mode, res, result, error }) => {
					if (result && !error) {
					  if (result.supported) {
						isSupported = true;
						
						if (result.smooth) {
						  isSmooth = true;
						}
						
						if (result.powerEfficient) {
						  isPowerEfficient = true;
						}
						
						if (!supportedModes.includes(mode)) {
						  supportedModes.push(mode);
						}
					  }
					}
				  });
				  
				  if (isSupported) {
					const codecName = codec.name;
					codecData.video[codecName] = codecData.video[codecName] || {};
					codecData.video[codecName].mediaCapabilities = true;
					codecData.video[codecName].scalabilityModes = supportedModes;
					
					// Assume MediaCapabilities is testing decoding
					if (detectionMode.decode) {
					  codecData.video[codecName].canDecode = true;
					}
					
					// If powerEfficient is true, this is a good indicator of hardware acceleration
					if (isPowerEfficient && codecData.video[codecName].hardwareAccelerated !== true) {
					  codecData.video[codecName].hardwareAccelerated = true;
					  codecData.video[codecName].details = (codecData.video[codecName].details || '') + 
						'MediaCapabilities reports this codec as power efficient, suggesting hardware acceleration. ';
					}
					
					// Store SDP format parameters if available
					if (codec.sdpFmtpLine && !codecData.video[codecName].profile) {
					  const profileMatch = codec.sdpFmtpLine.match(/profile-level-id=([0-9a-f]+)/i);
					  if (profileMatch) {
						codecData.video[codecName].profile = profileMatch[1];
					  }
					}
				  }
				} catch (error) {
				  addLog(`MediaCapabilities check failed for ${codec.name}: ${error.message}`, true);
				}
			  }
			} else {
			  addLog('MediaCapabilities API not supported by this browser');
			}
			
			addLog('Detection complete!');
			setProgress(100);
			
			// Re-enable the start button
			const startButton = dom.get('start-detection');
			startButton.disabled = false;
			startButton.textContent = 'Start Comprehensive Detection';
		  } catch (error) {
			showError(`Detection failed: ${error.message}`);
			addLog(`Detection error: ${error.message}`, true);
			console.error(error);
			
			// Re-enable the start button on error
			const startButton = dom.get('start-detection');
			startButton.disabled = false;
			startButton.textContent = 'Start Comprehensive Detection';
		  }
		}
    
	
    // Update the UI with detected codec information
    function updateUI() {
      // Clear existing content
      dom.get('video-codec-list').innerHTML = '';
      dom.get('audio-codec-list').innerHTML = '';
      
      // Add video codecs
      const videoCodecs = Object.keys(codecData.video);
      if (videoCodecs.length === 0) {
        dom.get('video-codec-list').innerHTML = '<p>No supported video codecs detected</p>';
      } else {
        videoCodecs.forEach(codec => {
          const info = codecData.video[codec];
          const item = createCodecItem(codec, info);
          dom.get('video-codec-list').appendChild(item);
        });
      }
      
      // Add audio codecs
      const audioCodecs = Object.keys(codecData.audio);
      if (audioCodecs.length === 0) {
        dom.get('audio-codec-list').innerHTML = '<p>No supported audio codecs detected</p>';
      } else {
        audioCodecs.forEach(codec => {
          const info = codecData.audio[codec];
          const item = createCodecItem(codec, info);
          dom.get('audio-codec-list').appendChild(item);
        });
      }
      
		dom.get('output').innerHTML += '\n--- DETECTION SUMMARY ---\n';
		dom.get('output').innerHTML += `Video Codecs: ${videoCodecs.join(', ')}\n`;
		dom.get('output').innerHTML += `Audio Codecs: ${audioCodecs.join(', ')}\n`;

		// Log hardware accelerated codecs
		const hwCodecs = videoCodecs.filter(codec => codecData.video[codec].hardwareAccelerated);
		dom.get('output').innerHTML += `Hardware Accelerated: ${hwCodecs.join(', ')}\n`;

		// Add H.264 profile information
		const h264Codecs = videoCodecs.filter(codec => 
		  codec.toLowerCase().includes('h264') || codec.toLowerCase().includes('avc'));
		if (h264Codecs.length > 0) {
		  dom.get('output').innerHTML += '\n--- H.264 PROFILES ---\n';
		  h264Codecs.forEach(codec => {
			const profile = codecData.video[codec].profile || 'Unknown';
			dom.get('output').innerHTML += `${codec}: profile-level-id=${profile}\n`;
		  });
		}
    }
	
	let detectionStarted = false;
	
	setTimeout(function() {
	  if (!detectionStarted) {
		dom.get('start-detection').click();
		addLog('Auto-starting detection...');
	  }
	}, 5000);
    
    // Start detection when button is clicked
    dom.get('start-detection').addEventListener('click', function() {
	  detectionStarted = true;
	  detectCodecs();
	});
  </script>
</body>
</html>